package ord2.exec;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.AbstractMap;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import ord2.convert.ConvertException;
import ord2.convert.Converter;
import ord2.convert.ConverterFactory;
import ord2.convert.ParametricConverter;
import ord2.core.Parameter;
import ord2.crawler.Crawler;
import ord2.crawler.CrawlerFactory;
import ord2.crawler.CrawlerListener;
import ord2.database.DatabaseConnector;
import ord2.database.DatabaseFactory;
import ord2.indexer.Indexer;
import ord2.indexer.IndexerFactory;
import ord2.log.LogSystem;
import ord2.notifier.Notifier;
import ord2.notifier.NotifierFactory;
import ord2.search.Searcher;
import ord2.search.SearcherFactory;
import ord2.webservice.WebService;
import ord2.webservice.WebServiceFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

/**
 *
 * @author José Ivan
 * @author Edviges
 */

public final class ConfigurationParser {
    
    private final static String[] cInterfaces  = {
                                    "ord2.core.Parametric",
                                    "ord2.convert.ParametricConverter"
                                };
    
    private static LinkedList<StartAction> actions = new LinkedList<StartAction>();
    private static LinkedList<ConnectAction> connect = new LinkedList<ConnectAction>();
    private static LinkedList<Node> notifierManager = new LinkedList<Node>();
    
    
    public static void processConfigurationXML(File configurationXML)
            throws MalformedFileException, SAXException, ParserConfigurationException,
            IOException {
        
        LogSystem.logMessage("Processing XML configuration file: "
                + configurationXML.getAbsolutePath());
        DocumentBuilder fact = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = fact.parse(configurationXML);
        Node n = doc.getFirstChild();
        
        if (n.getNodeName().equals("Configuration")) {
            
            NodeList docs = n.getChildNodes();
            int len  = docs.getLength();
            for (int i = 0; i < len; i++) {
                
                Node entry = docs.item(i);
                String name = entry.getNodeName();
                
                if (name.equals("Converter")) {
                    
                    processConverterNode(entry);
                } else if (name.equals("Searcher")) {
                    
                    processSearcherNode(entry);
                } else if (name.equals("Indexer")) {
                    
                    processIndexerNode(entry);
                } else if (name.equals("Start")) {
                    
                    processStartNode(entry);
                } else if (name.equals("Database")) {
                    
                    processDatabaseNode(entry);
                } else if (name.equals("WebService")) {
                    
                    processWebServiceNode(entry);
                } else if (name.equals("Crawler")) {

                    processCrawlerNode(entry);
                } else if (name.equals("Notifier")) {
                    
                    processNotifierNode(entry);
                } else if (name.equals("Connect")) {

                    processConnectNode(entry);
                } else if (name.equals("NotifierManager")) {
                    //Adiciona para depois ser contruído, pois deve ser o último
                    notifierManager.add(entry);
                }
            }
        } else {
            
            throw new MalformedFileException("Excepted Configuration tag, found: "
                    + n.getNodeName());
        }

        processConnectActions();
        processStartActions();
        for (Node nodeNotider : notifierManager) {
            
            processNotifierManagerNode(nodeNotider).start();
        }
    }
    
    @SuppressWarnings({"unchecked", "unchecked"})
    private static void processConverterNode(Node node) throws MalformedFileException {
        
        LogSystem.logMessage("Converter found...");
        Class<Converter> convClass = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classA = node.getAttributes().getNamedItem("class");
        LogSystem.logMessage("Converter name: " + name.getTextContent());
        if (name == null || classA == null) {
            
            throw new MalformedFileException("name and/or class tags are missing in" +
                    "Converter tag.");
        }
        
        try {
            
            convClass = (Class<Converter>) Class.forName(classA.getTextContent());
        } catch (ClassNotFoundException cnfe) {
            
            throw new MalformedFileException("Path to specified class doesn't exist");
        } catch (ClassCastException cce) {
            
            throw new MalformedFileException("Specified class is not a converter");                    
        }
        
        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(convClass, cInterfaces);
                
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This converter: " + name.getTextContent()
                    + " does not accept parameters");
        }

        try {
            
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (isParametric) {

                Parameter[] f = new Parameter[0];
                Constructor<Converter> cons = convClass.getConstructor(f.getClass());
                Object[] parsc = {
                                    pars
                                };
                ConverterFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            } else {

                ConverterFactory.getInstance().attachInstance(name.getTextContent(),
                        convClass.getConstructor().newInstance());
            }
        } catch (NoSuchMethodException e) {
            
            throw new MalformedFileException("Specified converter does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {
            
            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified in Converter tag is not valid.");
        }
    }
    
    private static void processSearcherNode(Node node) throws MalformedFileException {
        
        String[] pInterfaces = {
                                    "ord2.core.Parametric",
                                    "ord2.search.ParametricSearcher"
                                };
        
        LogSystem.logMessage("Searcher found...");
        Class<Searcher> convClass = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classA = node.getAttributes().getNamedItem("class");
        LogSystem.logMessage("Searcher name: " + name.getTextContent());
        
        if (name == null || classA == null) {
            
            throw new MalformedFileException("name and/or class tags are missing in" +
                    "Searcher tag.");
        }
        
        try {
            
            convClass = (Class<Searcher>) Class.forName(classA.getTextContent());
        } catch (ClassNotFoundException cnfe) {
            
            throw new MalformedFileException("Path to specified class doesn't exist");
        } catch (ClassCastException cce) {
            
            throw new MalformedFileException("Specified class is not a valid searher");                    
        }
        
        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(convClass, pInterfaces);
                
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This searcher: " + name.getTextContent()
                    + " does not accept parameters");
        }

        try {
            
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (isParametric) {

                Parameter[] f = new Parameter[0];
                Constructor<Searcher> cons = convClass.getConstructor(f.getClass());
                Object[] parsc = {
                                    pars
                                };
                SearcherFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            } else {

                SearcherFactory.getInstance().attachInstance(name.getTextContent(),
                        convClass.getConstructor().newInstance());
            }
        } catch (NoSuchMethodException e) {
            
            throw new MalformedFileException("Specified indexer does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {
            
            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified in Indexer tag is not valid.");
        }
    }
    
    private static Parameter[] getParameters(Node node) throws MalformedFileException {
        
        //Cria o array de parâmetros passados
        NodeList converterChilds = node.getChildNodes();
        int len = converterChilds.getLength();
        LinkedList<Parameter> pars = new LinkedList<Parameter>();
        for (int i = 0; i < len; i++) {
            
            Node entry = converterChilds.item(i);
            if (entry.getNodeName().equals("Parameter")) {
                
                Node parName = entry.getAttributes().getNamedItem("name");
                Node value = entry.getAttributes().getNamedItem("value");
                
                if (value == null || parName == null) {
                    
                    throw new MalformedFileException("name or value is missing in Parameter tag"
                            + " in Converter tag");
                }
                
                pars.add(new Parameter(parName.getTextContent(), value.getTextContent()));
            }
        }
        
        return pars.toArray(new Parameter[0]);
    }
    
    @SuppressWarnings("unchecked")
    private static <T> boolean isParametric(Class<T> convClass, String[] parametricInterfaces)
                        throws MalformedFileException {
        
        //Tenta carregar a instância descrita para esse conversor
        try {
            
            Class[] interfaces = convClass.getInterfaces();
            
            //Verifica se ele pode receber parâmetros
            for (Class c : interfaces) {
                
                for (String interfacet : parametricInterfaces) {
                 
                    if (interfacet.equals(c.getName())) {
                        
                        return true;
                    }
                }
            }
            
            Class sc = convClass.getSuperclass();
            //Sem mais super classe
            if (sc == null) {
                
                return false;
            }
            
            //Procura na superclass. Se a super classe for Parametrica essa também é
            return isParametric(sc, parametricInterfaces);
            
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified is not valid.");
        }
        
    }
    
    private static File[] processDocumentNode(Node node) throws MalformedFileException,
            IOException {

        LogSystem.logMessage("Document node found...");
        LinkedList<File> foundFiles = new LinkedList<File>();
        Node path = node.getAttributes().getNamedItem("path");
        LogSystem.logMessage("Document path: " + path.getTextContent());
        
        if (path == null) {
            
            throw new MalformedFileException("Missing path attribute in Document tag.");
        }
        
        NodeList childs = node.getChildNodes();
        int len = childs.getLength();
        boolean hadConvertion = false;
        
        for (int i = 0; i < len; i++) {
            
            Node n = childs.item(i);
            if (n.getNodeName().equals("Converter")) {
                
                //Verifica se há um caminho de saída ou cria um arquivo temporário
                Node converter = n.getAttributes().getNamedItem("converterName");
                Node out = n.getAttributes().getNamedItem("out");
                File fOut = null;
                
                if (out == null) {
                    
                    fOut = File.createTempFile("ord2", null);
                } else {
                    
                    fOut = new File(out.getTextContent());
                }
                
                if (converter == null) {
                    
                    throw new MalformedFileException("Missing converterName attribute in Document tag.");
                }
                
                Converter conv = ConverterFactory.getInstance()
                        .getAttachedInstance(converter.getTextContent());
                
                if (conv == null) {
                    
                    throw new MalformedFileException("Converter doesn't exists to perform convertion");
                }
                
                Parameter[] pars = getParameters(n);                
                if (pars.length > 0 && !isParametric(conv.getClass(), cInterfaces)) {
                    
                    throw new MalformedFileException("It's impossible to use parameters in this convertion");
                }
                
                try {
                
                    if (pars.length == 0) {
                    
                        LogSystem.logMessage("Converting... " + path.getTextContent() + " -> " + fOut.getAbsolutePath());
                        conv.convert(new File(path.getTextContent()), fOut);
                    } else {
                        
                        ((ParametricConverter) conv).convert(
                                new File(path.getTextContent()), fOut, pars);
                    }
                    
                    hadConvertion = true;
                    foundFiles.add(fOut);
                } catch (ConvertException ce) {
                    
                    System.err.println("There was a conversion error with: "
                            + path.getTextContent() + " using " + converter.getTextContent());
                }
                
            }
        }
        
        //Verifica se houve conversão se não joga o arquivo especificado em path
        if (!hadConvertion) {
            
            foundFiles.add(new File(path.getTextContent()));
        }
        
        return foundFiles.toArray(new File[0]);
    }
    
    private static NotifierManager processNotifierManagerNode(Node entry)
            throws MalformedFileException {
    
        LogSystem.logMessage("Notifier Manager found...");
        Node interval = entry.getAttributes().getNamedItem("interval");
        if (interval == null) {
        
            throw new MalformedFileException("Interval attribute missing in NotifierManager tag");
        }
        
        int intervalInt = 0;
        try {
        
            intervalInt = Integer.parseInt(interval.getTextContent());
        } catch (NumberFormatException nfe) {
            
            throw new MalformedFileException("Interval time specified in NotifierManager is not valid");
        }
        
        NotifierManager nm = new NotifierManager(intervalInt);
        NodeList childs = entry.getChildNodes();
        int len = childs.getLength();
        for (int i = 0; i < len; i++) {
            
            Node child = childs.item(i);
            String name = child.getNodeName();
            
            if (name.equals("Searcher")) {
                
                Node names = child.getAttributes().getNamedItem("name");
                if (names == null) {
                    
                    throw new MalformedFileException("Missing name in Searcher specification in NotifierManager tag");
                }
                
                Searcher s = SearcherFactory.getInstance()
                                .getAttachedInstance(names.getTextContent());
                if (s == null) {
                    
                    throw new MalformedFileException("Searcher specified ("
                            + names.getTextContent() + ") in NotifierManager was not found");
                }
                LogSystem.logMessage("Attaching searcher: " + names.getTextContent());
                nm.addSearcher(s);                
            } else if(name.equals("Database")) {
                
                Node named = child.getAttributes().getNamedItem("name");
                if (named == null) {
                    
                    throw new MalformedFileException("Missing name in Database specification in NotifierManager tag");
                }
                
                DatabaseConnector dc = DatabaseFactory.getInstance()
                        .getAttachedInstance(named.getTextContent());
                
                if (dc == null) {
                    
                    throw new MalformedFileException("Database specified ("
                            + named.getTextContent() + ") in NotifierManager was not found");
                }
                LogSystem.logMessage("Attaching database: " + named.getTextContent());
                nm.addDatabaseConnector(dc);
            } else if (name.equals("Notifier")) {
                
                Node tidn = child.getAttributes().getNamedItem("tid");
                Node notin = child.getAttributes().getNamedItem("name");
                
                if (tidn == null || notin == null) {
                    
                    throw new MalformedFileException("Missing tid and/or name for"
                            + "Notifier especification in Notifier tag.");
                }
                
                Notifier not = NotifierFactory.getInstance()
                                    .getAttachedInstance(notin.getTextContent());
                if (not == null) {
                    
                    throw new MalformedFileException("Notifier especificed in NotifierManager tag"
                            + " was not found. name = " + notin.getTextContent());
                }
                LogSystem.logMessage("Attaching notifier: " + notin.getTextContent()
                        + " will treat " + tidn.getTextContent());;
                nm.addNotifier(tidn.getTextContent(), not);
            } else {
                
                //TODO Add warning de ignoramento
            }
        }
        return nm;
    }
    
    private static File[] processDirectoryNode(Node node)
            throws MalformedFileException, IOException {
        
        LogSystem.logMessage("Directory node found...");
        Node path = node.getAttributes().getNamedItem("path");
        Node outPath = node.getAttributes().getNamedItem("outPath");
        final Node filter = node.getAttributes().getNamedItem("filter");
        String filterStr = filter != null
                            ?filter.getTextContent()
                            :"*";
        LinkedList<File> foundFiles = new LinkedList<File>();
        //Tá bom. Isso é uma gambiarra =}. Mas é pequena e só uma
        HashMap<String, AbstractMap.SimpleEntry<String, Parameter[]>> convs = new HashMap<String, AbstractMap.SimpleEntry<String, Parameter[]>>();
        
        if (outPath == null) {
            
            throw new MalformedFileException("Missing outPath attribute in Directory tag");
        } else if (path == null) {
            
            throw new MalformedFileException("Missing path attribute in Directory tag");
        }
        
      FileFilter ff = new FileFilter() {
            
            @Override
            public boolean accept(File f) {
                
                return f.getName().matches(filter.getTextContent());
            }
        };
        
        File[] files = new File(path.getTextContent()).listFiles(ff);
        
        //Busca conversores declarados
        NodeList childs = node.getChildNodes();
        int len = childs.getLength();
        
        for (int i = 0; i < len; i++) {
            
            Node n = childs.item(i);
            if (!n.getNodeName().equals("Converter")) {
                
                continue;
            }
            
            Node convN = n.getAttributes().getNamedItem("converterName");
            Node filterC = n.getAttributes().getNamedItem("filter");
            String filterSC = filterC == null
                              ?"*"
                              :filterC.getTextContent();
            if (convN == null) {
                
                throw new MalformedFileException("Missing converterName attribute in Converter tag inside Directory tag");
            }
            
            convs.put(convN.getTextContent(),
                    new AbstractMap.SimpleEntry<String, Parameter[]>(filterSC, getParameters(n)));
        }
        
        File dir = new File(outPath.getTextContent());
        //Executa conversão
        for (File f : files) {
            
            if (!f.getName().matches(filterStr)) {
            
                continue;
            }
            
            boolean hadConvertion = false;
            for (String cstr : convs.keySet()) {

                //Verifica se casa com filtro do conversor
                String filterC = convs.get(cstr).getKey();
                if (!f.getName().matches(filterC)) {
                    
                    continue;
                }
                
                Parameter[] cpars = convs.get(cstr).getValue();
                Converter conv = ConverterFactory.getInstance().getAttachedInstance(cstr);
                if (conv == null) {

                    throw new MalformedFileException("Converter specified to convertion is not found");
                }

                File tmpFile = File.createTempFile("ord2", null, dir);

                if (cpars.length == 0 && isParametric(conv.getClass(), cInterfaces)) {

                    throw new MalformedFileException("It's impossible to use parameters in this convertion");
                }

                boolean isParametric = isParametric(conv.getClass(), cInterfaces);
                if (cpars.length == 0 || !isParametric) {

                    try {

                        LogSystem.logMessage("Converting... " + f.getAbsolutePath()
                                + " -> " + tmpFile.getAbsolutePath());
                        conv.convert(f, tmpFile);
                        foundFiles.add(tmpFile);
                        hadConvertion = true;
                    } catch (ConvertException ce) {

                        System.err.println("There was a conversion error with: "
                            + f + " using " + cstr);
                    }
                } else if(isParametric) {

                    try {

                        ((ParametricConverter) conv).convert(f, tmpFile, cpars);
                        foundFiles.add(tmpFile);
                        hadConvertion = true;
                    } catch (ConvertException ce) {

                        System.err.println("There was a conversion error with: "
                            + f + " using " + cstr);
                    }                    
                }
            }
            
            //Se não tiver conversão adiciona o arquivo diretamente
            if (!hadConvertion) {
            
                foundFiles.add(f);
            }
        }
        
        return foundFiles.toArray(new File[0]);
    }
    
    private static void processIndexerNode(Node node)
            throws MalformedFileException, IOException {
        
        LogSystem.logMessage("Indexer found...");
        String[] iInterfaces = {
                                    "ord2.core.Parametric",
                                };
        Class<Indexer> convClass = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classA = node.getAttributes().getNamedItem("class");
        LogSystem.logMessage("Indexer name: " + name.getTextContent());
        if (name == null || classA == null) {
            
            throw new MalformedFileException("name and/or class tags are missing in" +
                    "Searcher tag.");
        }
        
        try {
            
            convClass = (Class<Indexer>) Class.forName(classA.getTextContent());
        } catch (ClassNotFoundException cnfe) {
            
            throw new MalformedFileException("Path to specified class doesn't exist");
        } catch (ClassCastException cce) {
            
            throw new MalformedFileException("Specified class is not a valid searher");                    
        }
        
        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(convClass, iInterfaces);
                
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This indexer: " + name.getTextContent()
                    + " does not accept parameters");
        }

        LinkedList<File> files = new LinkedList<File>();
        //Busca os documentos que farão parte deste indexer
        NodeList subNodes = node.getChildNodes();
        int l = subNodes.getLength();
        for (int i = 0; i < l; i++) {
            
            Node sao3damanhaeestousemcriatividadeparanomes = subNodes.item(i);
            String sname = sao3damanhaeestousemcriatividadeparanomes.getNodeName();
            if (sname.equals("Documents")) {
                
                NodeList estouprogramandonoaviao =
                        sao3damanhaeestousemcriatividadeparanomes.getChildNodes();
                int d = estouprogramandonoaviao.getLength();
                for (int k = 0; k < d; k++) {

                    Node snnode = estouprogramandonoaviao.item(k);
                    String snname = snnode.getNodeName();
                    if (snname.equals("Document")) {

                        File[] f = processDocumentNode(snnode);
                        files.addAll(Arrays.asList(f));
                    } else if (snname.equals("Directory")) {

                        File[] f = processDirectoryNode(snnode);
                        files.addAll(Arrays.asList(f));
                    }
                }
            }
        }
        
        try {
            
            File[] ffiles = files.toArray(new File[0]);
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (!isParametric) {

                Constructor<Indexer> cons = convClass.getConstructor(ffiles.getClass());
                Object[] parsc = {
                                    files
                                };
                IndexerFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            } else {

                Constructor<Indexer> cons = convClass.getConstructor(ffiles.getClass(), pars.getClass());
                Object[] parsc = {
                                    ffiles,
                                    pars
                                };
                IndexerFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            }
        } catch (NoSuchMethodException e) {
            
            throw new MalformedFileException("Specified indexer does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {
            
            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified in Indexer tag is not valid.");
        }        
    }
    
    private static void processStartNode(Node node) throws MalformedFileException {
        
        NodeList child = node.getChildNodes();
        int len = child.getLength();
        for (int i = 0; i < len; i++) {
            
            Node n = child.item(i);
            if (n.getNodeName().equals("start")) {
                
                Node type = n.getAttributes().getNamedItem("type");
                Node name = n.getAttributes().getNamedItem("name");
                if (type == null || name == null) {
                    
                    throw new MalformedFileException("type or name are missing in start tag");
                }
                
                String types = type.getTextContent();
                if (types.equals("indexer")) {
                    
                    actions.add(new StartAction(name.getTextContent(),
                            StartAction.StartType.INDEXER));
                } else if (types.equals("webservice")) {
                    
                    actions.add(new StartAction(name.getTextContent(),
                            StartAction.StartType.WEB_SERVICE));
                } else if (types.equals("crawler")) {
                    
                    actions.add(new StartAction(name.getTextContent(),
                            StartAction.StartType.CRAWLER));
                } else if (types.equals("database")) {
                    
                    actions.add(new StartAction(name.getTextContent(),
                            StartAction.StartType.DATABASE));
                } else {
                    
                    throw new MalformedFileException("type attribute for start tag is not valid");
                }
            }
        }
    }

    private static void processConnectNode(Node node) throws MalformedFileException {

        NodeList child = node.getChildNodes();
        int len = child.getLength();
        for (int i = 0; i < len; i++) {

            Node n = child.item(i);
            if (n.getNodeName().equals("CrawlerListener")) {

                Node type = n.getAttributes().getNamedItem("listenertype");
                Node name = n.getAttributes().getNamedItem("name");
                Node listener = n.getAttributes().getNamedItem("listener");
                if (type == null || name == null || listener == null) {

                    throw new MalformedFileException("type listener or or name are missing in CrawlerListener tag");
                }

                String types = type.getTextContent();
                if (types.equals("indexer")) {

                    connect.add(new ConnectAction(name.getTextContent(),
                            listener.getTextContent(),
                            ConnectAction.Type.INDEXER, ConnectAction.Type.CRAWLER));
                } else if (types.equals("crawler")) {

                    connect.add(new ConnectAction(name.getTextContent(),
                            listener.getTextContent(),
                            ConnectAction.Type.CRAWLER, ConnectAction.Type.CRAWLER));
                } else if (types.equals("database")) {

                    connect.add(new ConnectAction(name.getTextContent(),
                            listener.getTextContent(),
                            ConnectAction.Type.DATABASE, ConnectAction.Type.CRAWLER));
                } else if (types.equals("searcher")) {

                    connect.add(new ConnectAction(name.getTextContent(),
                            listener.getTextContent(),
                            ConnectAction.Type.SEARCHER, ConnectAction.Type.CRAWLER));
                } else {

                    throw new MalformedFileException("type attribute for CrawlerListener tag is not valid");
                }
            }
        }
    }
    
    private static void processDatabaseNode(Node node) throws MalformedFileException {
        
        LogSystem.logMessage("Database found...");
        String[] dbInter = {
                                "ord2.core.Parametric"
                            };
        Class<DatabaseConnector> classLoad = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classPath = node.getAttributes().getNamedItem("class");
        LogSystem.logMessage("Database name: " + name.getTextContent());
        if (name == null || classPath == null) {
            
            throw new MalformedFileException("name and/or class tags are missing in Database tag");
        }
        
        try {
            
            classLoad = (Class<DatabaseConnector>) Class.forName(classPath.getTextContent());
        } catch (ClassNotFoundException cnfe) {
            
            throw new MalformedFileException("Class path specified in Database tag was not found.");
        } catch (ClassCastException cce) {
            
            throw new MalformedFileException("Class specified in Database tag is not a valid DatabaseConnector.");
        }
        
        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(classLoad, dbInter);
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This database: " + name.getTextContent()
                    + " does not accept parameters");
        }
        
        try {
            
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (!isParametric) {

                Constructor<DatabaseConnector> cons = classLoad.getConstructor();
                DatabaseFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance());
            } else {

                Constructor<DatabaseConnector> cons = classLoad.getConstructor(pars.getClass());
                Object[] parsc = {
                                    pars
                                };
                DatabaseFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            }
        } catch (NoSuchMethodException e) {
            
            throw new MalformedFileException("Specified database connector does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {
            
            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified in Database tag is not valid.");
        }        
    }

    private static void processWebServiceNode(Node node) throws MalformedFileException {
        
        LogSystem.logMessage("WebService found...");
        String[] dbInter = {
                                "ord2.core.Parametric"
                            };
        Class<WebService> classLoad = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classPath = node.getAttributes().getNamedItem("class");
        LogSystem.logMessage("WebService name: " + name.getTextContent());
        if (name == null || classPath == null) {
            
            throw new MalformedFileException("name and/or class tags are missing in Database tag");
        }
        
        try {
            
            classLoad = (Class<WebService>) Class.forName(classPath.getTextContent());
        } catch (ClassNotFoundException cnfe) {
            
            throw new MalformedFileException("Class path specified in WebService tag was not found.");
        } catch (ClassCastException cce) {
            
            throw new MalformedFileException("Class specified in Database tag is not a valid WebService.");
        }
        
        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(classLoad, dbInter);
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This WebService: " + name.getTextContent()
                    + " does not accept parameters");
        }
        
        try {
            
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (!isParametric) {

                Constructor<WebService> cons = classLoad.getConstructor();
                WebServiceFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance());
            } else {

                Constructor<WebService> cons = classLoad.getConstructor(pars.getClass());
                Object[] parsc = {
                                    pars
                                };
                WebServiceFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            }
        } catch (NoSuchMethodException e) {
            
            throw new MalformedFileException("Specified WebService does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {
            
            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified in WebService tag is not valid.");
        }        
    }
    
    private static void processCrawlerNode(Node node) throws MalformedFileException {

        String[] dbInter = {
                                "ord2.core.Parametric"
                            };
        Class<Crawler> classLoad = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classPath = node.getAttributes().getNamedItem("class");
        Node interval = node.getAttributes().getNamedItem("interval");

        if (name == null || classPath == null || interval == null) {

            throw new MalformedFileException("name or class or interval atributes are missing in Crawler tag");
        }

        try {

            classLoad = (Class<Crawler>) Class.forName(classPath.getTextContent());
        } catch (ClassNotFoundException cnfe) {

            throw new MalformedFileException("Class path specified in Crawler tag was not found.");
        } catch (ClassCastException cce) {

            throw new MalformedFileException("Class specified in Crawler tag is not a valid Crawler.");
        }

        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(classLoad, dbInter);
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This Clawler: " + name.getTextContent()
                    + " does not accept parameters");
        }

        try {

            int intervali = Integer.parseInt(interval.getTextContent());
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (!isParametric) {

                Constructor<Crawler> cons = classLoad.getConstructor(int.class);
                CrawlerFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance());
            } else {

                Constructor<Crawler> cons = classLoad.getConstructor(pars.getClass(),
                        int.class);
                Object[] parsc = {
                                    pars,
                                    intervali
                                };
                CrawlerFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            }
        } catch (NoSuchMethodException e) {

            throw new MalformedFileException("Specified crawler does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {

            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (NumberFormatException nfe) {

            throw new MalformedFileException("Interval value in crawler tag is not valid.");
        } catch (Exception e) {

            throw new MalformedFileException("Class especified in Crawler tag is not valid.");
        }
    }
    
    private static void processNotifierNode(Node node) throws MalformedFileException {
        
        String[] dbInter = {
                                "ord2.core.Parametric"
                            };
        Class<Notifier> classLoad = null;
        Node name = node.getAttributes().getNamedItem("name");
        Node classPath = node.getAttributes().getNamedItem("class");
        
        if (name == null || classPath == null) {
            
            throw new MalformedFileException("name and/or class atributes are missing in Notifier tag");
        }
        
        try {
            
            classLoad = (Class<Notifier>) Class.forName(classPath.getTextContent());
        } catch (ClassNotFoundException cnfe) {
            
            throw new MalformedFileException("Class path specified in Notifier tag was not found.");
        } catch (ClassCastException cce) {
            
            throw new MalformedFileException("Class specified in Notifer tag is not a valid Notifer.");
        }
        
        Parameter[] pars = getParameters(node);
        boolean isParametric = isParametric(classLoad, dbInter);
        //Tem parâmetros, mas a classe não é paramétrica
        if (pars.length > 0 && !isParametric) {

            throw new MalformedFileException("This Notifier: " + name.getTextContent()
                    + " does not accept parameters");
        }
        
        try {
            
            //TODO Implementer verificação de nomes iguais para conversores diferentes
            if (!isParametric) {

                Constructor<Notifier> cons = classLoad.getConstructor();
                NotifierFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance());
            } else {

                Constructor<Notifier> cons = classLoad.getConstructor(pars.getClass());
                Object[] parsc = {
                                    pars
                                };
                NotifierFactory.getInstance().attachInstance(name.getTextContent(),
                        cons.newInstance(parsc));
            }
        } catch (NoSuchMethodException e) {
            
            throw new MalformedFileException("Specified notifier does not have"
                    + " a suitable constructor.");
        } catch (InvocationTargetException ivt) {
            
            throw new MalformedFileException("Creation Exception: " + ivt.getTargetException().getMessage());
        } catch (Exception e) {
            
            throw new MalformedFileException("Class especified in Notifier tag is not valid.");
        }        
    }
    
    private static void processStartActions() throws MalformedFileException {

        for (int i = 0; i < actions.size(); i++) {

            System.out.println(actions.get(i).name);

            StartAction sa = actions.get(i);
            switch (sa.type) {
                
                case INDEXER:
                    
                    Indexer ind = IndexerFactory.getInstance().getAttachedInstance(sa.name);
                    if (ind == null) {
                        
                        throw new MalformedFileException("Indexer specified in start action was not found");
                    }
                    
                    ind.processDocuments();
                    break;
                    
                case CRAWLER:

                    Crawler craw = CrawlerFactory.getInstance().getAttachedInstance(sa.name);
                    if (craw == null) {

                        throw new MalformedFileException("Crawler specified in start action was not found");
                    }

                    try {
                        
                        //FIXME Receber URL do XML
                        craw.addURL(null);
                        craw.start();
                    } catch (Throwable e) {

                        e.printStackTrace();
                    }
                    System.out.println("OUT");
                    break;
                    
                case WEB_SERVICE:
                    
                    WebService ws = WebServiceFactory.getInstance().getAttachedInstance(sa.name);
                    if (ws == null) {

                        throw new MalformedFileException("WebService specified in start action was not found");
                    }
                    
                    ws.start();
                    break;
                    
                case DATABASE:

                    final DatabaseConnector dbcon = DatabaseFactory.getInstance().getAttachedInstance(sa.name);

                    (new Thread() {
                        @Override
                        public void run() {


                            if (dbcon == null) {

                                //throw new MalformedFileException("Database connector specified in start action was not found");
                            }
                            try {

                                dbcon.connect();
                            } catch (Throwable t) {

                                //TODO Tratar erros de conexão =P
                            }

                        }
                    }).start();
                    System.out.println("OUTD");
                    break;
                    
                default:
                    
                    throw new MalformedFileException("type for start action is not valid.");
            }
        }
    }

    private static void processConnectActions() throws MalformedFileException {

        for (ConnectAction ca : connect) {

            switch (ca.instance_type) {

                case CRAWLER:

                    Crawler c = CrawlerFactory.getInstance().getAttachedInstance(ca.name);
                    if (c == null) {

                        throw new MalformedFileException("Crawler specified in CrawlerListener was not found");
                    }
                    switch (ca.listener_type) {

                        case INDEXER:

                            Indexer ind = IndexerFactory.getInstance().getAttachedInstance(ca.listener);
                            if (ind == null) {

                                throw new MalformedFileException("Crawler specified in CrawlerListener action was not found");
                            }

                            try {
                            
                                c.addListener((CrawlerListener) ind);
                            } catch (ClassCastException cce) {

                                throw new MalformedFileException("Listerner specified in CrawlerListener tag is not a valid CrawlerListener instance");
                            }
                        break;

                        case CRAWLER:
                            break;

                        case DATABASE:
                            break;

                        default:

                            throw new MalformedFileException("type for start action is not valid.");
                    }
                    
                    break;
            }
        }
    }
    
    private static class StartAction {
        
        public enum StartType {
        
            INDEXER,
            WEB_SERVICE,
            CRAWLER,
            DATABASE
        };
        
        public final String name;
        public final StartType type;
        
        public StartAction(String name, StartType type) {
            
            this.name = name;
            this.type = type;
        }
    }

    private static class ConnectAction {

        public enum Type {

            CRAWLER,
            INDEXER,
            SEARCHER,
            DATABASE
        };

        public final String name;
        public final Type instance_type;
        public final Type listener_type;
        public final String listener;

        public ConnectAction(String name, String listener, Type type, Type instance) {

            this.name = name;
            this.listener_type = type;
            this.listener = listener;
            this.instance_type = instance;
        }
    }
}
